
/*
以下接口并未按照时序
    reg[14:0] V;    (原是329开始变化,其实是328~329之间).现在变为328变化


时序处理
    at_bitL8  329开始变化,337为下一个


有关oam的计算
    一个主要的OAM sram      大小为0x100, 一共64个精灵
    一个辅助的OAM sram      大小为32,    一共8个精灵 (用于处理下一行扫描线)
        OAM内存结构为 [Y] [Pat] [Att] [X]   []对应每一个字节
        扫描线水平计数会从1~64   清除OAM sram的数据(填充$ff)
        扫描线水平计数从65~256   计算OAM的Y坐标数据
        扫描线水平计数冲261~320  读取OAM的tile数据到缓存内

    以下是当前渲染扫描线的数据.(经过辅助OAM sram处理后生成的)
        oam_tileL[0:7]      8个对应精灵的低位tile的8位数据      总共64位
        oam_tileH[0:7]      8个对应精灵的高位tile的8位数据      总共64位
        oam_pos[0:7]        8个对应精灵的8位坐标数据(0~255).    相当于8个0~256计数器.每绘制一个像素,则减一
        oam_pal0[0:7]       8个对应精灵的1位调色板数据低位
        oam_pal1[0:7]       8个对应精灵的1位调色板数据高位
        oam_att0[0:7]       8个对应精灵的属性
        oam_att1[0:7]       8个对应精灵的属性
        oam_prio[0:7]       8个对应精灵的优先级

    有关oam_pos设置从X=261开始.(相当于(X&0x7)==5) 依次为(261,269,277,285,293,301,309,317)

有部分是根据https://www.qmtpro.com/~nes/chipimages/visual2c02/ 时序实现
    有关sprite的实现.未参考visual2c02
    有关BG的实现,大部分的时序对应实现.(但实现并不是,只是简单的模拟像素时序.)
        visual2c02是以21MHz的频率实现,有关ppu_dat的引脚数据.可能是在半个像素的时候读取到指定寄存器
            有关PPU_ALE.则可能是半个像素后拉低(更实际的情况是在持续读的时候,高电平为100ns.低电平持续280ns).然后ppu_dat作为高阻态 (ALE在拉高的时候.ppu_dat指向的是ppu_addr[7:0])
        有关CVBS的输出.则以晶振的上升沿与下降沿更新数据.则为42MHz
            实际上42MHz分12个像素方波输出. 则3.56MHz(这个是CVBS的副载波频率)
            这12个像素其实对应的是调色板低4位的'h00~'h0d. 而调色板的高2位则是输出电平的高低位(其实就是亮度)
        有关CVBS的副载波.其实相当于如下(使用类似方波的形式实现模拟相位)
        v  :HLHLHLHLHLHL        晶振电平
        c  :123456789012        索引
            111111------        当color = 'h1
            22222------2        当color = 'h2
            3333------33        ..
            444------444        ..
            55------5555        ..
            6------66666        ..
            ------777777        ..
            -----888888-        ..
            ----999999--        ..
            ---AAAAAA---        ..
            --BBBBBB----        ..
            -CCCCCC-----        当color = 'hc
        color[5:4]只是对应亮度值(也就是高低电平的不同). 如上 - 表示低电平,  1,2,3,4表示高电平状态
            三个像素分别是 'h1,'h2,'h3  则时序如下
            111111------22222------23333------33        对应上图.但实际上有问题
            111111------22222------*3333------**        去掉一些像素时序模糊问题
            HHHHHHLLLLLLHHHHHLLLLLLHHHHHLLLLLLHH        实际上输出可能是这样子(需要修正)
        虽然最有效的是应该使用12个时钟实现像素相位.但NES的像素更新却是以8位更新.
            相当于晶振执行了12次像素处理.NES像素时钟确是以8个时钟更新
            |012345678901           CVBS像素显示相位时钟
            |111111------|
            |------------|
            |pix0    |pix2...       NES的像素读取时钟. (需要把数字平均)
            |111111-----*22222-     实际是像这样渲染.*表示可忽略
            |111111--****55----     渲染1,5像素时
                更详细的说明应该参考visual2c02的CVBS输出



有关task的说明
    task_ppu_rst:       重置按钮
    task_ppu_ctrl:      处理写入ppu的控制器
    task_ppu_mask:      处理写入ppu的mask
    task_ppu_statues    读取ppu的状态
    task_ppu_oamaddr    设置ppu的oam地址
    task_ppu_oamdata    写入oam数据.oam地址递增
    task_ppu_scroll     写入T寄存器.卷轴设置
    task_ppu_addr       写入T寄存器.设置ppu的数据写入位置
    task_ppu_data       通过ppu写入sram数据


*/

`define PPU_CTRL        'h00
`define PPU_MASK        'h01
`define PPU_STATUES     'h02
`define PPU_OAMADDR     'h03
`define PPU_OAMDATA     'h04
`define PPU_SCROLL      'h05
`define PPU_ADDR        'h06
`define PPU_DATA        'h07

module ppu (
    //ppu接口
    ppu_addr,
    ppu_dat,
    ppu_clk,
    ppu_rw,
    ppu_rd,
    ppu_cs,
    ppu_nmi,

    //cpu接口
    cpu_addr,
    cpu_dat,
    cpu_rw,

    //输出像素
    out_pixel,
    out_x,
    out_y,

    rstn,
);

    output reg[13:0] ppu_addr = 'd0;
    inout  wire[7:0]  ppu_dat;
    input  wire ppu_clk;
    output reg  ppu_rw = 1'b1;
    output reg  ppu_rd = 1'b1;
    output reg  ppu_nmi = 1'b1;
    input wire ppu_cs;      reg ppu_cs_old = 'd1;

    assign ppu_dat =
        (ppu_rw&~ppu_rd)?8'hzz:                     //ppu_rw 高电平,并且rd为低电平.则为高阻态
        ~ppu_rw?cpu_dat:                            //ppu_rw 为低电平时.直接输出cpu_dat
        8'hzz;

    //
    input wire[2:0] cpu_addr;
    inout wire[7:0] cpu_dat; reg [7:0] cpu_out_dat = 'd0;
    input wire cpu_rw;

`define CPU_WDATA(dat) \
    cpu_out_dat <= dat;
`define PPU_WDATA(dat)

    output wire[5:0] out_pixel;     //0~63
    output wire[8:0] out_x;
    output wire[8:0] out_y;
    input wire rstn;

    wire bg_addr = ppu_ctrl[4];
    assign out_x = hcounter;
    assign out_y = vcounter;
    reg [8:0] hcounter = 'd256;
    reg [8:0] vcounter = 'd261;
    reg [7:0] ppu_ctrl = 'h10;
    reg [7:0] ppu_mask = 'hff;
    reg [2:0] fine_x = 'd0;

    wire[13:0] name_att_addr = 14'h23C0 | {6'h00,V[11:10],V[9:7],V[4:2]};   //
    wire[13:0] pat_addr1 = {1'b0,bg_addr,nt_dat,1'b0,V[14:12]};                       //pat坐标
    wire[13:0] pat_addr2 = {1'b0,bg_addr,nt_dat,1'b1,V[14:12]};

    wire ppu_addr_inc32 = ppu_ctrl[2];
    wire ppu_gen_nmi = ppu_ctrl[7];

    reg [14:0] T = 'd0;
    reg [14:0] V = 'd0;
    reg w = 'd0;

    reg [7:0] last_ppu_dat = 'd0;
    reg [7:0] pat_lbit_buf = 'd0;
    reg [7:0] at_buf = 'd0;
    reg [7:0] nt_dat = 'd0;

    `ifdef NAT_BIT8
    reg [16:0]  at_bitH = 17'd0;
    reg [16:0]  at_bitL = 17'd0;
    `define set_at_bit(l,h) at_bitH[16] <= h; at_bitL[16] <= l;
    `define AT_BIT_SHIFT \
         at_bitH <= {at_bitH[16],at_bitH[16:1]}; \
         at_bitL <= {at_bitL[16],at_bitL[16:1]};
    `else
    reg [8:0]   at_bitH = 9'd0;
    reg [8:0]   at_bitL = 9'd0;
    `define set_at_bit(l,h) at_bitH[8] <= h; at_bitL[8] <= l;
    `define AT_BIT_SHIFT \
         at_bitH <= {at_bitH[8],at_bitH[8:1]}; \
         at_bitL <= {at_bitL[8],at_bitL[8:1]};
     `endif

    reg  last_V1 = 'd0;
    reg[15:0]  color_addr = 'd0;
    wire V1 = V[1];
    wire [3:0] at_att_datH = V[6]?at_buf[7:4]:at_buf[3:0];
    //wire [1:0] at_att_datL = ~V[1]?at_att_datH[3:2]:at_att_datH[1:0];
    wire [1:0] at_att_datL = ~last_V1?at_att_datH[1:0]:at_att_datH[3:2];        //需要修改,因为V提前处理了

    wire at_bitH8 = at_att_datL[1];
    wire at_bitL8 = at_att_datL[0];

    wire [7:0] pat_datH = tileH[7:0];
    wire [7:0] pat_datL = tileL[7:0];

    //现在暂时不处理背景层与精灵层的优先级
    wire [3:0] color =
        (fine_x==3'd0)?{at_bitH[0],at_bitL[0],pat_datH[0],pat_datL[0]}:          //因为pat_datH在赋值的时候.已经高低位翻转
        (fine_x==3'd1)?{at_bitH[1],at_bitL[1],pat_datH[1],pat_datL[1]}:
        (fine_x==3'd2)?{at_bitH[2],at_bitL[2],pat_datH[2],pat_datL[2]}:
        (fine_x==3'd3)?{at_bitH[3],at_bitL[3],pat_datH[3],pat_datL[3]}:
        (fine_x==3'd4)?{at_bitH[4],at_bitL[4],pat_datH[4],pat_datL[4]}:
        (fine_x==3'd5)?{at_bitH[5],at_bitL[5],pat_datH[5],pat_datL[5]}:
        (fine_x==3'd6)?{at_bitH[6],at_bitL[6],pat_datH[6],pat_datL[6]}:
        (fine_x==3'd7)?{at_bitH[7],at_bitL[7],pat_datH[7],pat_datL[7]}:4'd0
    ;

    wire spr_display = (oam_cur_draw_tileH[0]|oam_cur_draw_tileL[0]);           //只要不为空.则显示
    wire [3:0] spr_color = {oam_cur_draw_pal /*2bit*/,oam_cur_draw_tileH[0],oam_cur_draw_tileL[0]};

    //混合颜色
    wire [4:0] mix_color = spr_display?{1'b1,spr_color}:{1'b0,color};


    reg [5:0] pal_ram['h1f:'h00];
    initial begin
        pal_ram['h00] <= 'h22;
        pal_ram['h01] <= 'h29;
        pal_ram['h02] <= 'h1A;
        pal_ram['h03] <= 'h0F;
        pal_ram['h04] <= 'h22;
        pal_ram['h05] <= 'h36;
        pal_ram['h06] <= 'h17;
        pal_ram['h07] <= 'h0F;
        pal_ram['h08] <= 'h22;
        pal_ram['h09] <= 'h30;
        pal_ram['h0a] <= 'h21;
        pal_ram['h0b] <= 'h0F;
        pal_ram['h0c] <= 'h22;
        pal_ram['h0d] <= 'h07;
        pal_ram['h0e] <= 'h17;
        pal_ram['h0f] <= 'h0F;
        pal_ram['h10] <= 'h22;
        pal_ram['h11] <= 'h16;
        pal_ram['h12] <= 'h27;
        pal_ram['h13] <= 'h18;
        pal_ram['h14] <= 'h0F;
        pal_ram['h15] <= 'h1A;
        pal_ram['h16] <= 'h30;
        pal_ram['h17] <= 'h27;
        pal_ram['h18] <= 'h0F;
        pal_ram['h19] <= 'h16;
        pal_ram['h1a] <= 'h30;
        pal_ram['h1b] <= 'h27;
        pal_ram['h1c] <= 'h0F;
        pal_ram['h1d] <= 'h0F;
        pal_ram['h1e] <= 'h36;
        pal_ram['h1f] <= 'h17;
    end
    assign out_pixel = pal_ram[mix_color];


    /////////////////////////////////////////////////////
    //定义oam
    reg[15:0] tmp_reg;
    reg[7:0] oam['hff:0];
    reg[7:0] oamS['d63:0];              //
    reg[7:0] oam_pos['d7:'d0];
    reg[5:0] oam_toS_write = 'd0;       //写入辅助内存坐标 0~63
    reg[8:0] oam_read_addr = 'd0;       //oam读取位置
    reg[7:0] oam_tileL['d7:'d0];
    reg[7:0] oam_tileH['d7:'d0];
    reg      oam_att0['d7:'d0];
    reg      oam_att1['d7:'d0];
    reg      oam_cur_is_vert = 'd0;
    reg      oam_cur_is_hori = 'd0;
    reg[1:0] oam_pal['d7:'d0];
    reg oam_over = 'd0;
    wire oam_end = oam_read_addr>='h100;
    wire oam_tile_in1000 = ppu_ctrl[3];
    wire [31:0] cur_oam = {oam['d3],oam['d2],oam['d1],oam['d0]};
    wire [31:0] cur_oamS =  {oamS['d3],oamS['d2],oamS['d1],oamS['d0]};
    wire [31:0] cur_oamS2 = {oamS['d7],oamS['d6],oamS['d5],oamS['d4]};

    reg[7:0] oam_cur_draw_tileL;        //当前绘制的oam tileL数据
    reg[7:0] oam_cur_draw_tileH;        //当前绘制的oam tileH数据
    reg[1:0] oam_cur_draw_pal;          //当前绘制的调色板
    reg      oam_cur_draw_prio;         //当前绘制的优先级

    wire[7:0] oam_read = oam[oam_read_addr];
    wire[8:0] oam_eval_Y = ((vcounter-{1'b0,oam_read}));
    wire oam_hit_Y = oam_eval_Y < 8'd8 && oam_eval_Y >= 'd0;

    reg vblank =        'd0;
    reg spri_0_hit =    'd0;
    reg spri_over =     'd0;
    wire[7:0] ppu_status = {vblank,spri_0_hit,spri_over,5'h00};

`define OAM_POS_DEX \
    oam_pos['d0] <= oam_pos['d0] - 1; \
    oam_pos['d1] <= oam_pos['d1] - 1; \
    oam_pos['d2] <= oam_pos['d2] - 1; \
    oam_pos['d3] <= oam_pos['d3] - 1; \
    oam_pos['d4] <= oam_pos['d4] - 1; \
    oam_pos['d5] <= oam_pos['d5] - 1; \
    oam_pos['d6] <= oam_pos['d6] - 1; \
    oam_pos['d7] <= oam_pos['d7] - 1;

    integer i;
    initial begin
        $readmemh("ppu_oam.hex",oam);
    end

    task task_ppu_ctrl;
    begin
        if(~cpu_rw) ppu_ctrl <= cpu_dat;        //ppu控制器
    end endtask

    task task_ppu_mask;
    begin
        if(~cpu_rw) ppu_mask <= cpu_dat;        //ppu的数据
    end endtask

    task task_ppu_statues;
    begin
        if(cpu_rw)begin `CPU_WDATA(ppu_status); w <= 'd0; /*w需要初始化*/ end
    end endtask

    task task_ppu_oamaddr;begin
        if(~cpu_rw) oam_read_addr <= cpu_dat;
    end endtask

    task task_ppu_oamdata;begin
        if(~cpu_rw)begin oam[oam_read_addr]<=cpu_dat;  oam_read_addr<=oam_read_addr+8'd1; end
    end endtask

    task task_ppu_scroll;begin
        if(~cpu_rw)begin
            if(~w)begin T[4:0]<= cpu_dat[7:3];fine_x<= cpu_dat[2:0]; end //第一次写入
            else begin T[9:5]<=cpu_dat[7:3]; T[14:12]<= cpu_dat[2:0];end
            w <= ~w;
        end
    end endtask

    task task_ppu_addr;begin
        if(~cpu_rw)begin
            if(~w)begin T[15:8] <= cpu_dat;     end //第一次写入
            else begin T[7:0] <= cpu_dat;V <= {T[15:8],cpu_dat}; end //第二次写入
            w <= ~w;
        end
    end endtask

    task task_ppu_data;begin
        if(~cpu_rw)begin `PPU_WDATA(cpu_dat); end
        else begin `CPU_WDATA(last_ppu_dat);  end
        V <= V + ppu_addr_inc32?16'h20:16'd1;
    end endtask

    /////////////////////////////////////////////////////
`define TILE_SHIFT \
    tileH <= {1'b1,tileH[15:1]}; \
    tileL <= {1'b1,tileL[15:1]};
    reg [15:0] tileH = 16'd0;
    reg [15:0] tileL = 16'd0;

    wire proc_tile = (vcounter<240|vcounter>=260)&&(hcounter<256||(hcounter>='d320&&hcounter<'d337));

    always @(negedge ppu_clk) begin
        if(~ppu_rd)begin
            last_ppu_dat <= ppu_dat;
        end
    end

    task task_ppu_rst; begin
        ppu_ctrl <= 8'h10;
        ppu_mask <= 8'hff;
        hcounter <= 'd0;
        vcounter <= 'd0;
        ppu_cs_old <= 'd1;
    end endtask;

    always @(posedge ppu_clk)begin
        if(~rstn)begin task_ppu_rst; end
        else begin
            ////////////////////////////////////////////////////////////////////
            //cpu读写
            if(ppu_cs_old!=ppu_cs)begin
                //因为nes的1.7MHz的时序比ppu像素5.8MHz快.只需要一次读写
                ppu_cs_old <= ppu_cs;
                if(~ppu_cs)begin
                    case (cpu_addr)
                    `PPU_CTRL    :task_ppu_ctrl   ();
                    `PPU_MASK    :task_ppu_mask   ();
                    `PPU_STATUES :task_ppu_statues();
                    `PPU_OAMADDR :task_ppu_oamaddr ();
                    `PPU_OAMDATA :task_ppu_oamdata ();
                    `PPU_SCROLL  :task_ppu_scroll ();
                    `PPU_ADDR    :task_ppu_addr   ();
                    `PPU_DATA    :task_ppu_data   ();
                    default:;
                    endcase
                end
            end
            ////////////////////////////////////////////////////////////////////
            if(hcounter<340)begin
                hcounter <= hcounter + 9'd1;
            end
            else begin
                hcounter <= 9'd0;
                //$display("===%d",vcounter);
                if(vcounter<261)begin
                    vcounter <= vcounter + 9'd1;
                end
                else begin
                    vcounter <= 9'd0;
                    hcounter <= 9'd0;
                    T <= 15'd0;
                end
            end

            //开始渲染
            if(hcounter<'d242)begin
            end
            else if(hcounter=='d0)begin
                ppu_rd <= 'b1;      //等于0时为高
            end
            else begin
                //'d244时, rd为低电平
                ppu_rd <= ~hcounter[0];
            end

            if(hcounter[2:1]=='d1)
                last_V1 <= V[1];

            //复制
            //临时使用初始化
            if(hcounter=='d334)begin
                oam_toS_write <= 'd0;
            end

            /////////////////////////////////////////////////////////////
            //oam处理
            if(vcounter<240)begin
                ppu_rd <= ~hcounter[0];
                if(hcounter[0]==1'b0)begin
                    //类似命名表的处理方案
                    if(hcounter>='d256&&hcounter<'d320)begin
                        case (hcounter[2:1])
                            'd0:begin
                                //260,261
                                //如果是hcounter=='d260则是垃圾数据
                                if(~oam_cur_is_hori)
                                    oam_tileH[oam_toS_write[4:2]-1] <= {last_ppu_dat[0],last_ppu_dat[1],last_ppu_dat[2],last_ppu_dat[3],last_ppu_dat[4],last_ppu_dat[5],last_ppu_dat[6],last_ppu_dat[7]};
                                else
                                    oam_tileH[oam_toS_write[4:2]-1] <= last_ppu_dat;

                                ////////////////////////////////
                                //正式开始
                                oam_pos[oam_toS_write[4:2]]   <= oamS[oam_toS_write + 'd3];
                                oam_att0[oam_toS_write[4:2]]  <= oamS[oam_toS_write + 'd2][6];          //水平翻转
                                oam_att1[oam_toS_write[4:2]]  <= oamS[oam_toS_write + 'd2][7];          //垂直翻转
                                //有关垂直与水平翻转可以直接计算tile的位置
                                oam_cur_is_vert <= oamS[oam_toS_write + 'd2][7];
                                oam_cur_is_hori <= oamS[oam_toS_write + 'd2][6];
                                //
                                oam_pal[oam_toS_write[4:2]]   <= oamS[oam_toS_write + 'd2][1:0];        //调色板

                                tmp_reg <= oam_toS_write[4:2];
                            end
                            'd1:begin
                                //262,263
                                //处理
                            end
                            'd2:begin
                                //264,265
`define V_OAM_Y ({vcounter[3],vcounter[2],vcounter[1],vcounter[0]} - {oamS[oam_toS_write][3],oamS[oam_toS_write][2],oamS[oam_toS_write][1],oamS[oam_toS_write][0]})
`define V_OAM_Y_VFILP (`V_OAM_Y ^ 4'b0111)
                                if(oam_cur_is_vert)begin
                                    ppu_addr <= {2'b00,oam_tile_in1000,oamS[oam_toS_write + 1],4'h0} + `V_OAM_Y_VFILP;
                                end
                                else begin
                                    ppu_addr <= {2'b00,oam_tile_in1000,oamS[oam_toS_write + 1],4'h0} + `V_OAM_Y; /*获得tile L数据*/
                                end
                            end
                            'd3:begin
                                //266,267
                                if(~oam_cur_is_hori)
                                    oam_tileL[oam_toS_write[4:2]] <= {last_ppu_dat[0],last_ppu_dat[1],last_ppu_dat[2],last_ppu_dat[3],last_ppu_dat[4],last_ppu_dat[5],last_ppu_dat[6],last_ppu_dat[7]};
                                else
                                    oam_tileL[oam_toS_write[4:2]] <= last_ppu_dat;

                                if(oam_cur_is_vert)
                                    ppu_addr = {2'b00,oam_tile_in1000,oamS[oam_toS_write + 1],4'h8} + `V_OAM_Y_VFILP;
                                else
                                    ppu_addr = {2'b00,oam_tile_in1000,oamS[oam_toS_write + 1],4'h8} + `V_OAM_Y; /*获得tile H数据*/


                                // if(oamS[oam_toS_write]<'he0)begin
                                //     $write("%d,[%02X]%04X,",oamS[oam_toS_write],oam_toS_write,ppu_addr);
                                // end

                                oam_toS_write <= oam_toS_write + 5'd4;
                            end
                            default:;
                        endcase
                    end
                end
            end
            //////////////////////////////////////////////////////////////
            if(proc_tile)begin
                /*扫描线小于240 或扫描线等于260.  hcounter<256,或则hcounter小于d337*/
                ppu_rd <= ~hcounter[0];
                ///////////////////////////////////////////////////////////////////////////////////////////////////////////
                //处理OAM的数据
                if(hcounter<'d240)begin
                    //只在小于240处理oam数据
                    `OAM_POS_DEX;
                    oam_cur_draw_tileH <= {1'b0,oam_cur_draw_tileH[7:1]};       //需要位移
                    oam_cur_draw_tileL <= {1'b0,oam_cur_draw_tileL[7:1]};       //需要位移
                    for(i=7;i>=0;i=i-1)begin
                        if(oam_pos[i]=='d0)begin
                            //复制到当前复制的数据
                            //优先级是从0到7
                            oam_cur_draw_tileH <= oam_cur_draw_tileH | oam_tileH[i];
                            oam_cur_draw_tileL <= oam_cur_draw_tileL | oam_tileL[i];
                            oam_cur_draw_pal <= oam_pal[i];
                        end
                    end
                end


                ///////////////////////////////////////////////////////////////////////////////////////////////////////////
                if(hcounter<'d64)begin
                    oamS[oam_toS_write] <= 8'hff;       //填充为FF
                    oam_toS_write <= oam_toS_write + 5'd1;      //下一个填充地址
                    oam_over <= 1'b0;
                    oam_read_addr <= 'd0;
                end
                else if(hcounter<'d256)begin
                    //开始计算
                    if(~oam_end)begin
                        if(oam_toS_write[1:0]=='d0)begin
                            //对齐,表示需要判断精灵的Y坐标
                            if(oam_hit_Y)begin
                                //把数据复制到缓存中
                                if(oam_toS_write[5:2]==4'b1111)begin
                                    //溢出,设置溢出
                                    oam_over <= 1'b1;
                                end
                                else begin
                                    //没有溢出.继续
                                    oam_read_addr <= oam_read_addr + 8'd1;
                                    oamS[oam_toS_write] <= oam_read;
                                    oam_toS_write <= oam_toS_write + 5'd1;
                                end
                            end
                            else begin
                                //下一个oam
                                oam_read_addr <= oam_read_addr + 8'd4;
                            end
                        end
                        else begin
                            //直接复制3个数据
                            oamS[oam_toS_write] <= oam_read;
                            oam_read_addr <= oam_read_addr + 8'd1;
                            oam_toS_write <= oam_toS_write + 5'd1;
                        end
                    end

                    if(hcounter=='d255)begin
                        oam_toS_write <= 'd0;
                    end
                end

                `AT_BIT_SHIFT;
                if(hcounter[0]==1'b0)begin
                    //321开始
                    case (hcounter[2:1])
                        'd0:begin
                            ppu_addr <= {2'b10,V[11:0]};
                            tileL[7:0] <= tileL[8:1];
                            tileH[7:0] <= tileH[8:1];
                            `set_at_bit(at_bitL8,at_bitH8);
                            color_addr <= name_att_addr;

                            if(hcounter!='d0)begin
                                tileH[15:8] <= {last_ppu_dat[0],last_ppu_dat[1],last_ppu_dat[2],last_ppu_dat[3],last_ppu_dat[4],last_ppu_dat[5],last_ppu_dat[6],last_ppu_dat[7]};
                                tileL[15:8] <= {pat_lbit_buf[0],pat_lbit_buf[1],pat_lbit_buf[2],pat_lbit_buf[3],pat_lbit_buf[4],pat_lbit_buf[5],pat_lbit_buf[6],pat_lbit_buf[7]};
                            end
                        end
                        'd1:begin nt_dat        <= last_ppu_dat; ppu_addr <= name_att_addr;     `TILE_SHIFT; end
                        'd2:begin at_buf        <= last_ppu_dat; ppu_addr <= pat_addr1;         `TILE_SHIFT; end
                        'd3:begin

                            pat_lbit_buf  <= last_ppu_dat; ppu_addr <= pat_addr2;         `TILE_SHIFT;
                        end
                        default:;
                    endcase
                end
                else begin
                    `TILE_SHIFT;
                    case (hcounter[2:1])
                        'd0:begin   end
                        'd1:begin   end
                        'd2:begin   end
                        'd3:begin  {V[10],V[4:0]} <= {V[10],V[4:0]} + 6'd1;  end
                        default:;
                    endcase
                end
                //$write("%d",color);
            end
            else if((vcounter<'d240||vcounter>=261)) begin
                //处理垂直
                if(hcounter=='d256)begin
                    if(V[14:12]!='d7)begin
                        V[14:12] <= V[14:12] + 1;
                    end
                    else begin
                        V[14:12] <= 3'd0;
                        if(V[9:5]==5'd29)begin
                            //因为是240行扫描线
                            V[9:5] <= 5'd0;
                            V[11] <= ~V[11];
                        end
                        else begin
                            V[9:5] <= V[9:5] + 5'd1;
                        end
                    end
                end
                else if(hcounter=='d257)begin
                    V[4:0] <= T[4:0];
                    V[10] <= T[10];
                end
                else if(vcounter=='d261)begin
                    if(hcounter>='d280&&hcounter<305)begin
                        //需要把T复制到V中
                        V <= T;
                    end
                end

            end
        end

    end

endmodule